﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Text.RegularExpressions;
using BrasilFramework.Controle.Silverlight.Comum;
using BrasilFramework.Controle.Silverlight.Conversores;

namespace BrasilFramework.Controle.Silverlight
{

    /// <summary>
    /// Extensão do <see cref="System.Windows.Controls.TextBox"/> com vários tipos de formatação e filtros.
    /// Controle ainda possui Bind tipado para trabalhar com números.
    /// </summary>
    public class TextBox : System.Windows.Controls.TextBox
    {

        #region Propriedade

        public static readonly DependencyProperty FormatoPropriedade = DependencyProperty.Register("Formato",
                                                                                                   typeof(Formato),
                                                                                                   typeof(TextBox),
                                                                                                   new PropertyMetadata(Formato.Texto));

        public static readonly DependencyProperty CasasDecimaisPropriedade = DependencyProperty.Register("CasasDecimais",
                                                                                                   typeof(int),
                                                                                                   typeof(TextBox),
                                                                                                   new PropertyMetadata(
                                                                                                       0));

        public static readonly DependencyProperty NegativoPropriedade = DependencyProperty.Register("Negativo",
                                                                                                   typeof(bool),
                                                                                                   typeof(TextBox),
                                                                                                   new PropertyMetadata(
                                                                                                       false));

        public static readonly DependencyProperty MascaraPropriedade = DependencyProperty.Register("Mascara",
                                                                                                   typeof(string),
                                                                                                   typeof(TextBox),
                                                                                                   new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty InteiroPropriedade = DependencyProperty.Register("Inteiro",
                                                                                                   typeof(int?),
                                                                                                   typeof(TextBox),
                                                                                                   new PropertyMetadata(null, OnInteiroChanged));

        public static readonly DependencyProperty NumeroPropriedade = DependencyProperty.Register("Numero",
                                                                                                   typeof(decimal?),
                                                                                                   typeof(TextBox),
                                                                                                   new PropertyMetadata(null, OnNumeroChanged));

        public static readonly DependencyProperty CapitalizacaoPropriedade = DependencyProperty.Register("Capitalizacao",
                                                                                                   typeof(TipoCapitalizacao),
                                                                                                   typeof(TextBox),
                                                                                                   new PropertyMetadata(TipoCapitalizacao.MaiusculaMinuscula, OnTipoCapitalizacaoChanged));



        [TypeConverter(typeof(InteiroConversor))]
        public int? Inteiro
        {
            get { return (int?)GetValue(InteiroPropriedade); }
            set { SetValue(InteiroPropriedade, value); }
        }

        [TypeConverter(typeof(DecimalConversor))]
        public decimal? Numero
        {
            get { return (decimal?)GetValue(NumeroPropriedade); }
            set { SetValue(NumeroPropriedade, value); }
        }

        public TipoCapitalizacao Capitalizacao
        {
            get { return (TipoCapitalizacao)GetValue(CapitalizacaoPropriedade); }
            set { SetValue(CapitalizacaoPropriedade, value); }
        }

        public new string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        /// <summary>
        /// Formato do campo
        /// </summary>
        [Category("Formatação")]
        public Formato Formato
        {
            get { return (Formato)GetValue(FormatoPropriedade); }
            set { SetValue(FormatoPropriedade, value); }
        }

        /// <summary>
        /// Número de casas decimais
        /// </summary>
        [Category("Formatação - Número")]
        public int CasasDecimais
        {
            get { return (int)GetValue(CasasDecimaisPropriedade); }
            set { SetValue(CasasDecimaisPropriedade, value); }
        }

        /// <summary>
        /// Permite valores negativos
        /// </summary>
        [Category("Formatação - Número")]
        public bool Negativo
        {
            get { return (bool)GetValue(NegativoPropriedade); }
            set { SetValue(NegativoPropriedade, value); }
        }

        /// <summary>
        /// Máscara de formatação
        /// </summary>
        [Category("Formatação - Personalizado"), Description("# = Numero; ? = Letra; * = Qualquer caractere")]
        public string Mascara
        {
            get { return (string)GetValue(MascaraPropriedade); }
            set { SetValue(MascaraPropriedade, value); }
        }

        #endregion

        #region Eventos

        private static void OnInteiroChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TextBox controle = (TextBox)d;
            controle.Dispatcher.BeginInvoke(delegate
                                                {
                                                    controle.Text = (e.NewValue == null)
                                                                             ? string.Empty
                                                                             : ((int?)e.NewValue).ToString();
                                                });
        }

        public event PropertyChangedCallback NumeroChanged;

        private static void OnNumeroChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TextBox controle = (TextBox)d;

            controle.Dispatcher.BeginInvoke(delegate
                                                {

                                                    if (controle.NumeroChanged != null)
                                                        controle.NumeroChanged(d, e);

                                                    if (e.NewValue == null)
                                                    {
                                                        controle.Text = string.Empty;
                                                        return;
                                                    }

                                                    controle.Text = EventoTeclado.FormatarNumero(
                                                                    ((decimal)e.NewValue).ToString(),
                                                                    controle.CasasDecimais, controle.Negativo);
                                                });
        }

        private static void OnTipoCapitalizacaoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TextBox controle = (TextBox)d;

            controle.Dispatcher.BeginInvoke(delegate
                                                {
                                                    if (e.NewValue == null)
                                                    {
                                                        controle.Text = string.Empty;
                                                        return;
                                                    }

                                                    controle.Text = ConfigurarCapitalizacao(controle.Text, (TipoCapitalizacao)e.NewValue);
                                                });
        }

        private void TextoFocado(object sender, RoutedEventArgs e)
        {
            TextBox controle = (TextBox)sender;
            if (!IsEnabled || string.IsNullOrEmpty(controle.Text)) return;
            controle.Select(0, controle.Text.Length);
        }

        private void FormatarNumeroAreaTransferencia(object sender, KeyEventArgs e)
        {
            System.Windows.Controls.TextBox controle = (System.Windows.Controls.TextBox)sender;

            if (!controle.IsEnabled || controle.IsReadOnly || e == null || !EventoTeclado.VerificarTeclaAreaTransferencia(e.Key)) return;

            if (!Regex.IsMatch(controle.Text, ExpressaoRegular.RE_NUMERO))
            {
                ConfigurarNumero(string.Empty);
                e.Handled = true;
            }
            else
                ConfigurarNumero(EventoTeclado.FormatarNumero(controle.Text, CasasDecimais, Negativo));
        }

        private static string ConfigurarCapitalizacao(string texto, TipoCapitalizacao tipoCapitalizacao)
        {
            switch (tipoCapitalizacao)
            {
                case TipoCapitalizacao.Maiuscula:
                    return texto.ToUpper();
                case TipoCapitalizacao.Minuscula:
                    return texto.ToLower();
                default:
                    return texto;
            }
        }

        private void DefinirEventosMascara()
        {
            TextChanged += CampoCapitalizacao;

            if (Formato == Formato.Texto && string.IsNullOrEmpty(Mascara)) return;

            GotFocus += TextoFocado;

            switch (Formato)
            {
                case Formato.Numero:
                    KeyDown += EventoTeclado.CampoNumerico;
                    KeyUp += FormatarNumeroAreaTransferencia;
                    break;
                case Formato.CPF:
                case Formato.CEP:
                case Formato.Telefone:
                    KeyDown += EventoTeclado.CampoApenasNumero;
                    TextChanged += TextBox_TextChanged;
                    break;
            }
        }

        public static void CampoCapitalizacao(object sender, TextChangedEventArgs e)
        {
            var controle = (TextBox)sender;

            int indice = controle.SelectionStart;
            controle.Text = ConfigurarCapitalizacao(controle.Text, controle.Capitalizacao);
            controle.SelectionStart = indice;
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            System.Windows.Controls.TextBox controle = (System.Windows.Controls.TextBox)sender;
            controle.Text = EventoTeclado.Formatar(controle.Text, RecuperarFormato(Formato));

            controle.SelectionStart = controle.Text.Length;
        }

        #endregion

        #region Método Apoio

        private void ConfigurarNumero(string texto)
        {
            Text = texto;

            if (CasasDecimais == 0)
            {
                int inteiro;
                Inteiro = (int.TryParse(texto, out inteiro)) ? inteiro : (int?)null;
            }
            else
            {
                decimal numero;
                Numero = (decimal.TryParse(texto, out numero)) ? numero : (decimal?)null;
            }
        }

        private void Formatar(object sender, RoutedEventArgs e)
        {
            TextBox controle = (TextBox)sender;

            switch (Formato)
            {
                case Formato.Numero:
                    ConfigurarNumero(EventoTeclado.FormatarNumero(controle.Text, CasasDecimais, Negativo));
                    break;
            }
        }

        private static string RecuperarFormato(Formato formato)
        {
            string mascara;

            switch (formato)
            {
                case Formato.CEP:
                    mascara = "##.###-##";
                    break;
                case Formato.CPF:
                    mascara = "###.###.###-##";
                    break;
                case Formato.Telefone:
                    mascara = "(##) ####-####";
                    break;
                default:
                    mascara = string.Empty;
                    break;
            }

            return mascara;
        }

        #endregion

        #region Construtor

        public TextBox()
        {
            Loaded += TextBox_Loaded;
        }

        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);

            switch (Formato)
            {
                case Formato.Numero:
                    Formatar(this, e);
                    break;
            }
        }

        private void TextBox_Loaded(object sender, RoutedEventArgs e)
        {
            DefinirEventosMascara();
        }

        #endregion
    }

    public enum Formato
    {
        Texto,
        Numero,
        CEP,
        CPF,
        Telefone
    }

    public enum TipoCapitalizacao
    {
        MaiusculaMinuscula,
        Maiuscula,
        Minuscula
    }

}